Django Crispy Forms で Bootstrap を使ってみよう
インストールと準備
拡張モジュールなので次のようにインストールします。
code: bash
$ pip install django-crispy-forms
これを説明するためのアプリケーション crispy を作成しましょう。
code: bash
$ python manage.py startapp crispy
プロジェクトのsettings.py にある INSTALLED_APPS にcrispy と crispy_forms を追加します。
code: Python
INSTALLED_APPS = [
...
'crispy',
'crispy_forms',
]
プロジェクトの urls.py に crispy を追加します。
code: python
urlpatterns = [
# ...
path('crispy/', include('crispy.urls')),
]
テンプレートパック
django-crispy-formsには、テンプレートパックと呼ばれるさまざまなCSSフレームワークのサポートが組み込まれています。
uni-form:uni-form は見栄えの良いよく構造化された高度にカスタマイズ可能なフォーム プロジェクトの settings.py にCRISPY_TEMPLATE_PACKのデフォルトテンプレートパックを設定できます。
code: settings.py
CRISPY_TEMPLATE_PACK = 'bootstrap4'
ただし、crispy-forms には静的ファイルは含まれていません。
使用するテンプレートパックに応じて、対応するCSSフレームワークをダウンロードして配置するか、CDN(Content Delivery Network) から使用するバージョンを指定するようにします。
CDNの指定方法は cdnjs.com を使うと簡単に知ることができます。
例えば、bootstrap4 を CDN から読み込むためには、ベーステンプレートbase.html を次のように定義します。
<link>タグを <head>タグの内側に他のスタイルシートよりも先に記述します。
Bootstrap はモバイル用にコードを最適化し, 必要に応じて CSS メディアクエリを使ってコンポーネントのスケールアップを実現しています。 これを有効にするためには、viewport 用メタタグを <head>タグに追加しておきます。
code: crispy/templates/crispy_base.html
<!doctype html>
<html lang="ja">
<head>
<meta charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
</head>
<body>
<div class="container">
{% block content %}
{% endblock %}
</div>
</body>
</html>
サンプルのフォーム
code: crispy/models.py
from django.db import models
class User(models.Model):
name = models.CharField(max_length=32)
email = models.EmailField(blank=True)
nickname = models.CharField(max_length=32, blank=True)
about_you = models.TextField(blank=True)
ビューは汎用ビューを使いましょう。
code: crispy/view.py
from django.views.generic import CreateView
from .models import User
class UserCreateView(CreateView):
model = User
fields = ('name', 'email', 'nickname', 'about_you')
template_name = 'crispy_user_form.html'
テンプレートは次のようになります。
code: crispy/templates/crispy_user_form.html
{% extends "crispy_base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% crispy form %}
{% endblock %}
これらをcrispy/urls.py に登録します。
code: crispy/urls.py
from django.urls import path, include
from . import views
app_name = 'crispy'
urlpatterns = [
path('add/', views.UserCreateView.as_view(), name='user_create'),
]
データベースも作成しておきます。
code: bash
$ python manage.py makemigrations
python manage.py migrate
実行すると次のようにレンダリングされます。
https://gyazo.com/13a31f9820471fa78ad5ac79004802f1
CrispyFomsが指定した Bootstrap4 クラスを使用してフォームを作成しています。
code: HTML source
<form method="post" > <input type="hidden" name="csrfmiddlewaretoken" value="UCXQb0WuXaPamd5aTIkEf3loPNgbmE2bO10cv48iL1tUjtqQeTtBETwd137r1dO6"> <div id="div_id_name" class="form-group"> <label for="id_name" class=" requiredField">
Name<span class="asteriskField">*</span> </label> <div class=""> <input type="text" name="name" maxlength="32" class="textinput textInput form-control" required id="id_name"> </div> </div> <div id="div_id_email" class="form-group"> <label for="id_email" class="">
Email
</label> <div class=""> <input type="email" name="email" maxlength="254" class="emailinput form-control" id="id_email"> </div> </div> <div id="div_id_nickname" class="form-group"> <label for="id_nickname" class="">
Nickname
</label> <div class=""> <input type="text" name="nickname" maxlength="32" class="textinput textInput form-control" id="id_nickname"> </div> </div> <div id="div_id_about_you" class="form-group"> <label for="id_about_you" class="">
About you
</label> <div class=""> <textarea name="about_you" cols="40" rows="10" class="textarea form-control" id="id_about_you">
</textarea> </div> </div> </form>
Crispy フィルター
Crispyフィルターを使用すると、divタグベースのフィールドを使用してフォームまたはフォームセットをレンダリングできます。
crispy/templates/user_form.html ではじめに{% load crispy_forms_tags %} で crispy_forms を使えるようにし、{{ form | crispy }} でフォームに適用します。
code: crispy/templates/crispy_user_form.html
{% extends "crispy_base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
</form>
{% endblock %}
https://gyazo.com/355f186a3fa701c31b048ceafc1220d6
ただし、crispyフィルターはとても便利ですが、Django Forms の組み込みメソッドas_table、 as_ul 、as_pと同様にアウトプットの調整は十分にはできません。
as_crispy_field フィルタ
as_crispy_fieldフィルタを使うとフィールドごとにCSSスタイルを適用させることができるようになります。また、それぞれのフィールドは divタグのclassに Bootstrap の Gridレイアウト を使って位置や幅を調整することができるようになります。 code: templates/user_form.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post" novalidate>
{% csrf_token %}
<div class="row">
<div class="col align-self-center">
{{ form.name|as_crispy_field }}
{{ form.email|as_crispy_field }}
{{ form.nickname|as_crispy_field }}
{{ form.about_you|as_crispy_field }}
</div>
</div>
<button type="submit" class="btn btn-success">Save User</button>
</form>
{% endblock %}
https://gyazo.com/ef1dc5d3509620d6c72b699b772dae08
Girdレイアウトの横幅を分割するスタイルのルールの概要は次のようになります。
<div class="container">か<div class="container-fluid">の中に記述
<div class="row">の中に記述
<div class="col-{prefix}-{columns}">の形式で記述
{columns}は合計値が12になるように設定
FormHelperクラス
はじめに例示した{% crispy form %}では、フォームを表示することができますが、サブミットボタンはありません。これは、django-crispy-formsは、フォームのレンダリング動作を定義するためのFormHelperクラスを提供していて、FormHelperを使って、フォームの属性とそのレイアウトすることができるからです。
FormHelper クラスを使うためには、まず次のようにして、UserFormクラスをインスタンス化するための準備をします。
super() を使用してベースクラスのコンストラクターを呼び出し、コンストラクターをオーバーライドしています。
テンプレートは次のようにします。
code: crispy/templates/crispy_user_form.html
{% extends "crispy_base.html" %}
{% load crispy_forms_tags %}
{% block content %}
{% crispy form %}
{% endblock %}
forms.py に FormHelperを追加します。
code: crispy/forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from .models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'nickname', 'about_you')
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'id-userform'
self.helper.form_method = 'post'
self.helper.layout.append(Submit('save', 'Save User'))
ビューは次のようにform_classでフォームを指定します。
code: crispy/views.py
from django.views.generic import CreateView
from .models import User
from .forms import UserForm
class UserCreateView(CreateView):
model = User
form_class = UserForm
template_name = 'crispy_user_form.html'
form_classを使用するときは汎用ビューで個別にモデルクラスのフィールドを指定できないことに留意してください。
https://gyazo.com/9075ebdc0dac4d1db4f0b57642bf66d3
テンプレートファイルでは何も定義していませんが、
FormHelperで指定したサブミットボタンが表示されましたね。
FormHelper でフォームのレイアウト
こんどはフォームのレイアウトをもう少しカスタマイズしてみましょう。
code: crispy/forms.py
from django import forms
from django.urls import reverse_lazy
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit, Layout, Div, Fieldset
from crispy_forms.bootstrap import Field
from .models import User
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ('name', 'email', 'nickname', 'about_you')
def __init__(self, *args, **kwargs):
super(UserForm, self).__init__(*args, **kwargs)
self.helper = FormHelper(self)
self.helper.form_id = 'id-userform'
self.helper.form_method = 'post'
self.helper.form_class = 'form-horizontal'
# self.helper.form_action = reverse_lazy('crispy:list_user')
self.helper.add_input(Submit('save', 'Save User',
css_class='btn-success'))
self.helper.layout = Layout(
Fieldset('User Name',
Field('name', placeholder='user name for login',
css_class="some-class"),
Field('email', placeholder="Your Email address"),),
Fieldset('Profile',
Field('nickname', placeholder="Your nickname",
css_class="some-class"),
Field('about_you', placeholder="Your Profile"),))
https://gyazo.com/4059690a45cfd36cd70d09b8acb17c4f
独特なCSSの記述スタイルを使うことなく、Pythonコードとして Bootstrap などCSSフロントエンドフレームワークを使えることができるようになります。
参考: